/*
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "modelmanager_p.h"
#include "core/desktopdatahandler.h"
#include "core/appmanager.h"
#include "taskbarmanager.h"
#include "appcategorymanager.h"

ModelManagerPrivate::ModelManagerPrivate(ModelManager *parent)
    : dataHandler(new DesktopDataHandler())
    , appManager(new AppManager())
    , taskBarManager(new TaskBarManager(parent))
    , q_ptr(parent)
{
    Q_Q(ModelManager);

    untitledGroupCount = confMgr.untitledGroupCount();

    QThread *backendThread = new QThread;
    dataHandler->moveToThread(backendThread);
    connect(backendThread, &QThread::started, dataHandler, &DesktopDataHandler::init);
    connect(dataHandler, &DesktopDataHandler::finished, backendThread, &QThread::quit);
    connect(backendThread, &QThread::finished, dataHandler, &DesktopDataHandler::deleteLater);
    connect(backendThread, &QThread::finished, backendThread, &QThread::deleteLater);
    connect(qApp, &QGuiApplication::aboutToQuit, dataHandler, &DesktopDataHandler::finished);

    connect(dataHandler, &DesktopDataHandler::desktopData, q, &ModelManager::initDesktopData);
    connect(dataHandler, &DesktopDataHandler::categoriesData, q, &ModelManager::categories);

    connect(dataHandler, &DesktopDataHandler::appUninstalledFromGroup, q, &ModelManager::removeItemFromGroup);
    connect(dataHandler, &DesktopDataHandler::appUninstalled, q, &ModelManager::removeItemWithoutGroup);
    connect(dataHandler, &DesktopDataHandler::appInstalled, q, &ModelManager::itemInstalled);

    connect(q, &ModelManager::groupNameChanged, dataHandler, &DesktopDataHandler::setGroupName);
    connect(q, &ModelManager::updateGroup, dataHandler, &DesktopDataHandler::updateGroup);
    connect(q, &ModelManager::createGroup, dataHandler, &DesktopDataHandler::createGroup);
    connect(q, &ModelManager::removeGroup, dataHandler, &DesktopDataHandler::removeGroup);
    connect(q, &ModelManager::uninsatllApp, dataHandler, &DesktopDataHandler::uninsatllApp);
    connect(q, &ModelManager::replaceGroupIdWithAppId, dataHandler, &DesktopDataHandler::replaceGroupIdWithAppId);
    connect(q, &ModelManager::itemStateChanged, dataHandler, &DesktopDataHandler::itemStateChanged);
    connect(q, &ModelManager::updateHomeScreenPage, dataHandler, &DesktopDataHandler::updateHomeScreenPage);

    connect(dataHandler, &DesktopDataHandler::iconChanged, q, &ModelManager::iconThemeChanged);
    connect(dataHandler, &DesktopDataHandler::appCategoryChanged, q, &ModelManager::changeAppCategory);
    connect(dataHandler, &DesktopDataHandler::taskbarAppPosExchanged, q, &ModelManager::onTaskbarAppIdPosExchanged);
    connect(dataHandler, &DesktopDataHandler::itemIdMovedToTaskbar, q, &ModelManager::onItemIdMovedToTaskbar);
    connect(dataHandler, &DesktopDataHandler::itemIdTakedFromTaskbar, q, &ModelManager::onItemIdTakedFromTaskbar);
    connect(dataHandler, &DesktopDataHandler::itemIdTakedFromDesktop, q, &ModelManager::onItemIdTakedFromDesktop);
    connect(dataHandler, &DesktopDataHandler::itemIdTakedFromGroup, q, &ModelManager::onItemIdTakedFromGroup);

    backendThread->start();

    auto desktopMode = common::Singleton<Config>::GetInstance().desktopMode();
    if (desktopMode == Config::EduScreen) {
        needAddIcon = true;
    }
}

void ModelManagerPrivate::initTaskBarData(const QList<int> &ids, const QMap<int, UKUI::BaseItem *> &itemData)
{
    if (ids.isEmpty()) {
        return;
    }
    PageModel *groupPageModel = new PageModel;
    for (const auto &id : ids) {
        LauncherItem *launcherItem = createLauncherItem(LauncherItem::AppIcon, itemData.value(id), 0);
        if (!launcherItem) {
            continue;
        }
        if (isAppInTheWhiteList(launcherItem->desktopFile())) {
            launcherItem->setUninstallable(false);
        }
        groupPageModel->push_back(launcherItem);
    }
    if (groupModel.contains(0)) {
        for (auto item : *groupPageModel) {
            groupModel[0]->at(0)->push_back(item);
            if (isAppInTheWhiteList(item->desktopFile())) {
                item->setUninstallable(false);
            }
        }
        groupPageModel->deleteLater();
    } else {
        MultiPageModel *taskbarGroupModel = new MultiPageModel;
        taskbarGroupModel->push_back(groupPageModel);
        groupModel.insert(0, taskbarGroupModel);
    }
}

LauncherItem* ModelManagerPrivate::createItem(const int itemId, const int page)
{
    UKUI::BaseItem *baseItem =
            static_cast<UKUI::BaseItem *>(dataHandler->items().value(itemId));
    if (baseItem->getType() == UKUI::Type::Icon) {
        return createLauncherItem(LauncherItem::AppIcon, baseItem, page);
    }
    if (baseItem->getType() == UKUI::Type::Widget) {
        return createLauncherItem(LauncherItem::Widget, baseItem, page);
    }
    return nullptr;
}

LauncherItem* ModelManagerPrivate::createLauncherItem(LauncherItem::ItemType type,
                                                      UKUI::BaseItem *srcItem,
                                                      int page)
{
    if (!srcItem) {
        return nullptr;
    }
    LauncherItem *launcherItem = new LauncherItem;
    launcherItem->setId(srcItem->getId());
    launcherItem->setName(srcItem->getName());
    launcherItem->setPageIndex(page);
    switch (type) {
    case LauncherItem::AppIcon: {
        UKUI::IconItem *iconItem = static_cast<UKUI::IconItem *>(srcItem);
        if (!iconItem) {
            qWarning() << "Create app icon launcher item failed, the src icon item is empty.";
            return nullptr;
        }
        if (iconItem->getType() != UKUI::Type::Icon) {
            qWarning() << "Create app icon launcher item failed, the src icon item is not UKUI::Type::Icon type.";
            return nullptr;
        }
        launcherItem->setType(LauncherItem::AppIcon);
        launcherItem->setIcon(iconItem->getIconName());
        launcherItem->setDesktopFile(iconItem->getDesktopName());
        launcherItem->setRowSpan(1);
        launcherItem->setColumnSpan(1);
        launcherItem->setCategories(srcItem->getCategories());
        break;
    }
    case LauncherItem::Group: {
        UKUI::GroupItem *groupItem = static_cast<UKUI::GroupItem *>(srcItem);
        if (!groupItem) {
            return nullptr;
        }
        if (groupItem->getType() != UKUI::Type::Group) {
            qWarning() << "Create group launcher item failed, the src group item is not UKUI::Type::Group type.";
            return nullptr;
        }
        launcherItem->setType(LauncherItem::Group);
        launcherItem->setRowSpan(1);
        launcherItem->setColumnSpan(1);
        break;
    }
    case LauncherItem::Widget: {
        UKUI::Widget *widgetItem = static_cast<UKUI::Widget *>(srcItem);
        if (!widgetItem) {
            return nullptr;
        }
        if (widgetItem->getType() != UKUI::Type::Widget) {
            qWarning() << "Create widget launcher item failed, the src widget item is not UKUI::Type::widget type.";
            return nullptr;
        }
        launcherItem->setType(LauncherItem::Widget);
        if (widgetItem->getPlaceHolder().count() != 2) {
            qWarning() << "Create widget launcher item failed, the row or column is incorrent.";
            return nullptr;
        }
        launcherItem->setRowSpan(widgetItem->getPlaceHolder().at(0));
        launcherItem->setColumnSpan(widgetItem->getPlaceHolder().at(1));
        launcherItem->setQmlFile(widgetItem->getQmlFilePath());
        break;
    }
    default:
        return nullptr;
    }
    return launcherItem;
}

void ModelManagerPrivate::layoutForAddPlaceholderItem(int page, int groupId)
{
    layoutChangedPages.clear();
    MultiPageModel *model = groupId > 0 ? groupModel.value(groupId) : &launcherModel;
    int maxPageCount = groupId > 0 ? maxItemCountAllowedInPageOfGroup.value(groupId) : pageItemMaxCount;

    if (page == model->count() - 1 && gridCount(groupId, model->count()-1) > maxPageCount) {
        //最后一页添加占位符后网格总数超过24，需要添加一页
        PageModel *newPage = new PageModel;
        launcherModel.push_back(newPage);
    }
    // 超出当前页的item的数量的时候，将最后一个item移动至下一页
    // 如果最后一个item的是添加按钮的话，直接删除即可
    for (int pageIndex = page; pageIndex < model->count() - 1; ++pageIndex) {
        // pageIndex页所占网格的总数
        int pageGridCount = gridCount(groupId, pageIndex);
        int lastItemIndex = model->at(pageIndex)->count() - 1;

        while (pageGridCount > maxPageCount && lastItemIndex >= 0) {
            int startPage = pageIndex;
            int itemGridCount = model->at(pageIndex)->at(lastItemIndex)->rowSpan() *
                    model->at(pageIndex)->at(lastItemIndex)->columnSpan();
            qDebug() << "start page " << startPage;
            auto moveToNextPageItem = model->at(startPage)->takeLast();
            if (moveToNextPageItem->type() == LauncherItem::AddIcon) {
                moveToNextPageItem->deleteLater();
                --lastItemIndex;
                pageGridCount -= itemGridCount;
                continue;
            }
            moveToNextPageItem->setPageIndex(moveToNextPageItem->pageIndex() + 1);
            model->at(++startPage)->prepend(moveToNextPageItem);
            layoutChangedPages.push(startPage);

            --lastItemIndex;
            pageGridCount -= itemGridCount;
        }
        if (pageIndex == model->count() - 2 && gridCount(groupId, model->count()-1) > maxPageCount) {
            //倒数第二页的网格数超过24，将超出的item移动至最后一页，最后一页放不下，需要添加一页
            PageModel *newPage = new PageModel;
            launcherModel.push_back(newPage);
        }
    }
    qDebug() << "layoutChangedPages" << layoutChangedPages;
}

void ModelManagerPrivate::layoutForRemovePlaceholderItem()
{
    LauncherItem *moveItem = nullptr;
    MultiPageModel *model = placeholderItemInfo.groupId > 0 ?
                groupModel.value(placeholderItemInfo.groupId) : &launcherModel;
    while (!layoutChangedPages.isEmpty()) {
        int page = layoutChangedPages.pop();
        moveItem = model->at(page)->takeFirst();
        moveItem->setPageIndex(moveItem->pageIndex() - 1);
        model->at(--page)->append(moveItem);
    }
    // fix: 不显示“添加“按钮的分组内的item拖动至新建页时导致数组越界
#if 0
    if (model->last()->isEmpty()) {
        model->removeLast();
    }
#endif
}

void ModelManagerPrivate::layoutForReplacePlaceholderItem()
{
    LauncherItem *moveItem = nullptr;
    MultiPageModel *model = placeholderItemInfo.groupId > 0 ?
                groupModel.value(placeholderItemInfo.groupId) : &launcherModel;
    //
    while (layoutChangedPages.count() > 1) {
        int page = layoutChangedPages.pop();
        moveItem = model->at(page)->takeFirst();
        moveItem->setPageIndex(moveItem->pageIndex() - 1);

        model->at(--page)->append(moveItem);
    }

    // fix: 不显示“添加“按钮的分组内的item拖动至新建页时导致数组越界
#if 0
    if (model->last()->isEmpty()) {
        model->removeLast();
    }
#endif
}

LauncherItem *ModelManagerPrivate::createAddIconItem(int pageIndex, int state)
{
    if (!needAddIcon) {
        return nullptr;
    }
    LauncherItem *addItem = new LauncherItem;
    addItem->setId(0);
    addItem->setPageIndex(pageIndex);
    addItem->setType(LauncherItem::AddIcon);
    addItem->setIcon("qrc:/ui/images/ukui_add_default.svg");
    addItem->setName(tr("Add"));
    addItem->setRowSpan(1);
    addItem->setColumnSpan(1);
    addItem->setState(state);
    return addItem;
}

LauncherItem *ModelManagerPrivate::createAppIcon(int itemId, int pageIndex)
{
    LauncherItem *item = new LauncherItem;
    QMap<int, UKUI::BaseItem *> items = dataHandler->items();
    UKUI::IconItem *iconItem = static_cast<UKUI::IconItem *>(items.value(itemId));
    if (iconItem) {
        item->setId(itemId);
        item->setPageIndex(pageIndex);
        item->setName(iconItem->getName());
        item->setIcon(iconItem->getIconName());
        item->setDesktopFile(iconItem->getDesktopName());
        item->setColumnSpan(1);
        item->setRowSpan(1);
        item->setType(LauncherItem::AppIcon);
    }

    return item;
}

PageModel *ModelManagerPrivate::createPage(int pageIndex,
                                           int groupId,
                                           int state)
{
    PageModel *page = new PageModel;
    if (item(groupId)->containerEditable()) {
        LauncherItem *addItem = createAddIconItem(pageIndex, state);
        page->push_back(addItem);
    }
    return page;
}

//edu-screen
void ModelManagerPrivate::tryAddAddIcon(int containerId)
{
    if (!needAddIcon || !groupModel.contains(containerId)) {
        return;
    }
    MultiPageModel *model = groupModel.value(containerId);
    int maxItemCount = maxItemCountAllowedInPageOfGroup.value(containerId);
    for (int page = 0; page < model->count(); ++page) {
        if (model->at(page)->isEmpty()) {
            model->at(page)->push_back(createAddIconItem(page));
            continue;
        }
        if (model->at(page)->count() < maxItemCount &&
                model->at(page)->last()->type() != LauncherItem::AddIcon) {
            model->at(page)->push_back(createAddIconItem(page));
        }
    }
}

QPair<QString, QString> ModelManagerPrivate::categoryIcon(int category)
{
    QMetaEnum metaEnum = QMetaEnum::fromType<CategoryItem::Category>();
    const char *categoryName = metaEnum.valueToKey(category);

    return QPair<QString, QString>(QString("qrc:") + "/ui/images/categories/ukui_" + categoryName + "_default.svg",
                                   QString("qrc:") + "/ui/images/categories/ukui_" + categoryName + "_click.svg");
}

bool ModelManagerPrivate::itemExistsInGroup(int groupId, int itemId)
{
    Q_ASSERT(groupModel.contains(groupId));
    auto *model = groupModel.value(groupId);
    auto ret = iterateMultiPageModelWithItemReturnCallBack(model, [itemId](LauncherItem *item)->bool {
        return item->id() == itemId;
    });

    return ret;
}

int ModelManagerPrivate::itemGroupId(int itemId)
{
    auto groupModelIt = groupModel.constBegin();
    while (groupModelIt != groupModel.constEnd()) {
        auto ret = iterateMultiPageModelWithItemReturnCallBack(groupModelIt.value(), [itemId](LauncherItem *item)->bool {
                        return item->id() == itemId && item->type() == LauncherItem::AppIcon;
                });
        if (ret) {
            return groupModelIt.key();
        }
        ++ groupModelIt;
    }
    return -1;
}

QList<quint32> ModelManagerPrivate::itemIdsInGroup(int groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    QList<quint32> ids;
    iterateMultiPageModelWithItemCallBack(groupModel.value(groupId), [&ids](LauncherItem *item) {
        ids.append(item->id());
    });

    return ids;
}

void ModelManagerPrivate::removeItemFromCategory(int itemId)
{
    Q_Q(ModelManager);
    // 需要从全部和具体的分类中都删除
    int categoryItemPageCount = categoryItemsModel.count();
    for (int page=0; page<categoryItemPageCount; ++page) {
        int itemCountInPage = categoryItemsModel.at(page)->count();
        for (int itemIndex=0; itemIndex < itemCountInPage; ++itemIndex) {
            if (categoryItemsModel.at(page)->at(itemIndex)->id() == itemId) {
                categoryItemsModel.at(page)->removeAt(itemIndex);
                if (categoryItemsModel.at(page)->isEmpty()) {
                    int category = categoryOfAddtionPage(page);
                    updateCategoriesItemsPageIndex(page, -1);
                    categoryItemsModel.removeAt(page);
                    Q_EMIT q->categoryPageRemoved(category);
                    -- categoryItemPageCount;
                    // 如果当前分类下的应用全部被删除了，则删除该分类
                    for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
                        if (categoryModel.at(categoryIndex)->category() == category) {
                            if (categoryModel.at(categoryIndex)->pageCount() == 1) {
                                categoryModel.removeAt(categoryIndex);
                                Q_EMIT q->categoryRemoved(category);
                            } else if (categoryModel.at(categoryIndex)->pageCount() > 1) {
                                int newPageCount = categoryModel.at(categoryIndex)->pageCount() - 1;
                                categoryModel.at(categoryIndex)->setPageCount(newPageCount);
                            }
                        }
                    }
#if 0
                    int pageCount = 0;
                    for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
                        pageCount += categoryModel.at(categoryIndex)->pageCount();
                        if (pageCount > page) {
                            int newPageCount = categoryModel.at(categoryIndex)->pageCount() - 1;
                            if (newPageCount > 0) {
                                categoryModel.at(categoryIndex)->setPageCount(newPageCount);
                            } else {
                                int category = categoryModel.at(categoryIndex)->category();
                                categoryModel.removeAt(categoryIndex);
                                Q_EMIT q->categoryRemoved(category);
                            }
                            break;
                        }
                    }
#endif
                    break;
                }
                // move
                else {
                    while (page + 1 < categoryItemsModel.count() &&
                            categoryOfAddtionPage(page) == categoryOfAddtionPage(page + 1) &&
                            categoryOfAddtionPage(page) != -1 &&
                            !categoryItemsModel.at(page + 1)->isEmpty()) {
                        auto movedItem = categoryItemsModel.at(page + 1)->takeFirst();
                        movedItem->setPageIndex(movedItem->pageIndex() - 1);
                        categoryItemsModel.at(page ++)->push_back(movedItem);
                        if (categoryItemsModel.at(page)->isEmpty()) {
                            int category = categoryOfAddtionPage(page);
                            categoryItemsModel.removeAt(page);
                            for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
                                if (categoryModel.at(categoryIndex)->category() == category) {
                                    int newPageCount = categoryModel.at(categoryIndex)->pageCount() - 1;
                                    categoryModel.at(categoryIndex)->setPageCount(newPageCount);
                                }
                            }
                            Q_EMIT q->categoryPageRemoved(category);
                            -- categoryItemPageCount;
                            -- page;
                        }
                    }
                    break;
                }
            }
        }
    }
}

int ModelManagerPrivate::categoryOfAddtionPage(int page)
{
    Q_ASSERT(categoryItemsModel.count() > page && page >= 0);
    int pageStartIndex = 0, pageEndIndex = 0;
    for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
        int curCategoryCount = categoryModel.at(categoryIndex)->pageCount();
        pageStartIndex = pageEndIndex;
        pageEndIndex += curCategoryCount - 1;
        if (page >= pageStartIndex && page <= pageEndIndex) {
            return categoryModel.at(categoryIndex)->category();
        }
        ++ pageEndIndex;
    }
    return -1;
}

QList<int> ModelManagerPrivate::selectedIds() const
{
    QList<int> ids;
    for (int page=0; page<categoryItemsModel.count(); ++page) {
        int itemCount = categoryItemsModel.at(page)->count();
        for (int itemIndex=0; itemIndex<itemCount; ++itemIndex) {
            auto item = categoryItemsModel.at(page)->at(itemIndex);
            if (item->state() == LauncherItem::Selectable && item->selected()) {
                if (!ids.contains(item->id())) {
                    ids.push_back(item->id());
                }
            }
        }
    }
    qDebug() << __func__ << ids;
    return ids;
}

bool ModelManagerPrivate::categoryContainsItem(int itemId, int category)
{
    int startPage = 0, endPage = 0;
    bool foundCaegory = false;
    for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
        if (categoryModel.at(categoryIndex)->category() == category) {
            endPage = startPage + categoryModel.at(categoryIndex)->pageCount() - 1;
            foundCaegory = true;
            break;
        }
        startPage += categoryModel.at(categoryIndex)->pageCount();
    }
    if (foundCaegory) {
        for (int page=startPage; page<=endPage; ++page) {
            int itemCount = categoryItemsModel.at(page)->count();
            for (int index=0; index<itemCount; ++index) {
                if (categoryItemsModel.at(page)->at(index)->id() == itemId) {
                    return true;
                }
            }
        }
    }
    return false;
}

//edu-screen
void ModelManagerPrivate::autoPushBackItemForGroup(int itemId,
                                                  int groupId,
                                                  int startPageIndex)
{
    Q_ASSERT(groupModel.contains(groupId));
    if (groupModel.value(groupId)->count() <= startPageIndex) {
        groupModel.value(groupId)->push_back(createPage(startPageIndex, groupId, LauncherItem::Normal));
    }
    auto model = groupModel.value(groupId);
    int groupItemCount = model->at(startPageIndex)->count();
    if (groupItemCount < maxItemCountAllowedInPageOfGroup.value(groupId)) {
        model->at(startPageIndex)->insert(groupItemCount-1, createAppIcon(itemId, startPageIndex));
    } else if (groupItemCount == maxItemCountAllowedInPageOfGroup.value(groupId)) {
        if (model->at(startPageIndex)->last()->type() == LauncherItem::AddIcon) {
            model->at(startPageIndex)->removeLast();
            model->at(startPageIndex)->push_back(createAppIcon(itemId, startPageIndex));
            if (startPageIndex >= model->count()-1) {
                model->push_back(createPage(startPageIndex+1, groupId, LauncherItem::Normal));
            }
        } else {
            autoPushBackItemForGroup(itemId, groupId, ++startPageIndex);
        }
    } else {
        autoPushBackItemForGroup(itemId, groupId, ++startPageIndex);
    }
}

void ModelManagerPrivate::updateCategoriesItemsPageIndex(int startPageIndex, int diff)
{
    Q_ASSERT(startPageIndex >=0 && categoryItemsModel.count() > startPageIndex &&
             diff > - categoryItemsModel.count() + 1);
    for (int page=startPageIndex; page<categoryItemsModel.count(); ++page) {
        for (int itemIndex=0; itemIndex < categoryItemsModel.at(page)->count(); ++itemIndex) {
            auto item = categoryItemsModel.at(page)->at(itemIndex);
            item->setPageIndex(item->pageIndex() + diff);
        }
    }
}

void ModelManagerPrivate::initSelectedItemIds(int groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    currentSelectedIds.clear();
    for (int page=0; page<groupModel.value(groupId)->count(); ++page) {
        int itemCount = groupModel.value(groupId)->at(page)->count();
        for (int itemIndex=0; itemIndex<itemCount; ++itemIndex) {
            currentSelectedIds.push_back(groupModel.value(groupId)->at(page)->at(itemIndex)->id());
        }
    }
    qDebug() << __func__ << groupId << currentSelectedIds;
}

int ModelManagerPrivate::availableId()
{
    QMap<int, int> ids;

    auto getIdFunction = [&ids](const MultiPageModel &model) {
        for (const auto &pageItems : model) {
            for (const auto &items : *pageItems) {
                int id = items->id();
                ids.insert(id, id);
            }
        }
    };

    getIdFunction(launcherModel);

    auto it = groupModel.constBegin();
    while (it != groupModel.constEnd()) {
        getIdFunction(**it);
        ++ it;
    }

    if (ids.lastKey() == ids.count() - 1) {
        return ids.count() + 1;
    }

    int preId = 0;
    auto idIt = ids.constBegin();
    while (idIt != ids.constEnd()) {
        if (*idIt - preId > 1) {
            return preId + 1;
        }
        preId = *idIt;
        ++ idIt;
    }

    return ids.count() + 1;
}

LauncherItem *ModelManagerPrivate::item(int itemId)
{
    for (int page=0; page<launcherModel.count(); ++page) {
        for (int index=0; index<launcherModel.at(page)->count(); ++index) {
            if (launcherModel.at(page)->at(index)->id() == itemId) {
                return launcherModel.at(page)->at(index);
            }
        }
    }
    auto groupModelIt = groupModel.constBegin();
    while (groupModelIt != groupModel.constEnd()) {
        auto groupModel = groupModelIt.value();
        for (int page=0; page<groupModel->count(); ++page) {
            for (int itemIndex=0; itemIndex<groupModel->at(page)->count(); ++itemIndex) {
                if (groupModel->at(page)->at(itemIndex)->id() == itemId &&
                        groupModel->at(page)->at(itemIndex)->type() != LauncherItem::WindowIcon) {
                    return groupModel->at(page)->at(itemIndex);
                }
            }
        }
        ++ groupModelIt;
    }

    return nullptr;
}

void ModelManagerPrivate::setItemNameVisible(MultiPageModel *model, bool visible)
{
    if (!model) {
        return;
    }
    for (const auto &pageItems : *model) {
        for (const auto &item : *pageItems) {
            item->setNameVisible(visible);
        }
    }
}

void ModelManagerPrivate::replaceGroupWithAppIcon(int groupId)
{
    Q_Q(ModelManager);
    if (!groupModel.contains(groupId) || groupId == kTaskBarGroupId) {
        return;
    }
    auto model = groupModel.value(groupId);
    int itemCount = 0;
    int itemPageIndex = 0;
    for (int pageIndex = 0; pageIndex < model->count(); ++pageIndex) {
        for (int itemIndex = 0; itemIndex < model->at(pageIndex)->count(); ++itemIndex) {
            if (model->at(pageIndex)->at(itemIndex)->type() == LauncherItem::AppIcon) {
                itemPageIndex = pageIndex;
                ++itemCount;
            }
        }
    }
    if (itemCount == 0 || itemCount > 1) {
        return;
    }
    for (const auto &pageModel : launcherModel) {
        for (int index=0; index<pageModel->count(); ++index) {
            if (pageModel->at(index)->id() == groupId) {
                auto item = model->at(itemPageIndex)->takeFirst();
                *pageModel->at(index) = *item;
                item->deleteLater();
                pageModel->at(index)->setNameVisible(true);
                pageModel->at(index)->setState(globalState);
                pageModel->at(index)->setPageIndex(launcherModel.indexOf(pageModel));
                Q_EMIT q->replaceGroupIdWithAppId(groupId, pageModel->at(index)->id());
                groupModel.remove(groupId);
                Q_EMIT q->removeGroup(groupId);
                return;
            }
        }
    }
}

bool ModelManagerPrivate::groupIsEmpty(int groupId)
{
    if (groupId < 0 || !groupModel.contains(groupId)) {
        return false;
    }
    auto model = groupModel.value(groupId);
    int itemCount = 0;
    for (int pageIndex = 0; pageIndex < model->count(); ++pageIndex) {
        for (int itemIndex = 0; itemIndex < model->at(pageIndex)->count(); ++itemIndex) {
            if (model->at(pageIndex)->at(itemIndex)->type() == LauncherItem::AppIcon) {
                ++itemCount;
            }
        }
    }
    if (itemCount == 0) {
        return true;
    }
    return false;
}

int ModelManagerPrivate::findTheSameNumber(const QList<int> &set1, const QList<int> &set2)
{
    if (set1.isEmpty() || set2.isEmpty()) {
        return -1;
    }
    for (const auto &number1 : set1) {
        for (const auto &number2 : set2) {
            if (number1 == number2) {
                return number1;
            }
        }
    }
    return -1;
}

bool ModelManagerPrivate::placeholderAvailable()
{
    return placeholderItemInfo.itemIndex > - 1 &&
            placeholderItemInfo.pageIndex > -1 &&
            placeholderItemInfo.placeholderItem;
}

void ModelManagerPrivate::clearPlaceholderItemInfo()
{
    if (placeholderItemInfo.placeholderItem) {
        placeholderItemInfo.placeholderItem->deleteLater();
        placeholderItemInfo.placeholderItem = nullptr;
    }
    placeholderItemInfo.itemIndex = -1;
    placeholderItemInfo.pageIndex = -1;
    placeholderItemInfo.groupId = -1;
}

MultiPageModel *ModelManagerPrivate::availableModel(int groupId)
{
    if (groupId == -1) {
        return &launcherModel;
    }
    if (!groupModel.contains(groupId)) {
        qWarning() << "Can not find the group model of id: " << groupId;
        return nullptr;
    }
    return groupModel.value(groupId);
}

bool ModelManagerPrivate::itemIndexIsValid(int itemIndex, int pageIndex, const MultiPageModel *model)
{
    if (model == nullptr) {
        return false;
    }
    if (pageIndex < 0 || pageIndex >= model->count()) {
        return false;
    }
    if (itemIndex < 0 || itemIndex >= model->at(pageIndex)->count()) {
        return false;
    }
    return true;
}

bool ModelManagerPrivate::itemIndexOfInsertTaskbarIsValid(int itemIndex)
{
    if (!groupModel.contains(kTaskBarGroupId)) {
        return false;
    }
    auto model = groupModel.value(0);
    if (model == nullptr || model->count() == 0) {
        return false;
    }
    if (itemIndex < 0 || itemIndex > model->at(0)->count()) {
        return false;
    }
    return true;
}

bool ModelManagerPrivate::pageIndexIsValid(int pageIndex, const MultiPageModel *model)
{
    if (model == nullptr) {
        return false;
    }
    if (pageIndex < 0 || pageIndex >= model->count()) {
        return false;
    }
    return true;
}

void ModelManagerPrivate::groupAddPage(int groupId) {
    if (!groupModel.contains(groupId)) {
        return;
    }
    auto model = groupModel.value(groupId);
    if (model->last()->count() >= maxItemCountAllowedInPageOfGroup.value(groupId)) {
        PageModel *newPage = new PageModel;
        model->push_back(newPage);
    }
}

void ModelManagerPrivate::removeEmptyPage(MultiPageModel * const model)
{
    for (int i = 0; i < model->count(); i++) {
        if (model->at(i)->count() == 0) {
            model->removeAt(i);
            for (int j = i; j < model->count(); j++) {
                for (int itemIndex = 0; itemIndex < model->at(j)->count(); ++itemIndex) {
                    int groupItemPageIndex = model->at(j)->at(itemIndex)->pageIndex();
                    model->at(j)->at(itemIndex)->setPageIndex(groupItemPageIndex - 1);
                }
            }
            i--;
        }
    }
}

int ModelManagerPrivate::findGroupPageIndexFromDesktop(const int &groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    for (const auto &pageItems : launcherModel) {
        for (const auto &item : *pageItems) {
            if (item->type() == LauncherItem::Group && item->id() == groupId) {
                return item->pageIndex();
            }
        }
    }
    return -1;
}

void ModelManagerPrivate::removeGroupFromDesktop(const int &groupId)
{
    Q_Q(ModelManager);
    Q_ASSERT(groupModel.contains(groupId));
    iterateMultiPageModelWithItemIndexCallBack(&launcherModel, [q, this, groupId](
                                               LauncherItem *item, int pageIndex, int itemIndex) {
        if (item->id() == groupId && item->state() != LauncherItem::Popup) {
            groupModel.remove(groupId);
            Q_EMIT q->removeGroup(groupId);
            q->removeItem(itemIndex, pageIndex); //将分组从桌面删除
        }
    });
}

void ModelManagerPrivate::fillUpGroupEmptySpace(int page, int groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    if (groupId <= 0 && page >= groupModel.value(groupId)->count()-1 && page < 0) {
        return;
    }
    MultiPageModel *model = groupModel.value(groupId);
    for (int tempPage=page + 1; tempPage<model->count(); ++tempPage) {
        if (model->at(tempPage)->isEmpty()) {
            continue;
        }
        auto moveItem = model->at(tempPage)->takeFirst();
        moveItem->setPageIndex(tempPage - 1);
        model->at(tempPage-1)->push_back(moveItem);
    }
}

int ModelManagerPrivate::getGroupState(const int &groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    for (int page = 0; page < launcherModel.count(); page++) {
        for (int itemIndex = 0; itemIndex < launcherModel.at(page)->count(); itemIndex++) {
            if (launcherModel.at(page)->at(itemIndex)->id() == groupId) {
                return launcherModel.at(page)->at(itemIndex)->state();
            }
        }
    }
    return 0;
}

int ModelManagerPrivate::gridCount(const int &groupId, const int &pageIndex)
{
    MultiPageModel *model = groupId > 0 ? groupModel.value(groupId) : &launcherModel;
    if (model->empty() || pageIndex >= model->count() || pageIndex < 0) {
        qWarning() << __func__ << "groupId or pageIndex is incorrent";
        return -1;
    }
    auto pageModel = model->at(pageIndex);
    int pageItemCount = 0;
    for (int itemIndex = 0; itemIndex < pageModel->count(); ++itemIndex) {
        int itemCount = pageModel->at(itemIndex)->rowSpan() * pageModel->at(itemIndex)->columnSpan();
        pageItemCount += itemCount;
    }
    return pageItemCount;
}

void ModelManagerPrivate::getAppWhiteList()
{
    m_appWhiteList = appManager->getAppWhiteList();
}

bool ModelManagerPrivate::isAppInTheWhiteList(const QString &desktopname)
{
    for (int i = 0; i < m_appWhiteList.count(); ++i) {
        if (m_appWhiteList[i].isEmpty()) {
            continue;
        }
        if (desktopname.contains(m_appWhiteList[i])) {
            return true;
        }
    }
    return false;
}

int ModelManagerPrivate::itemIndexWithPinnedToTaskBar(int findInsertPosIndex)
{
    int taskbarItemCount = groupModel.value(kTaskBarGroupId)->at(0)->count();
    int findInsertPos = -1;
    int insertPos = -1;
    for (int i = 0; i < taskbarItemCount; ++i) {
        if (groupModel.value(kTaskBarGroupId)->at(0)->at(i)->type() != LauncherItem::WindowIcon) {
            ++findInsertPos;
            if (findInsertPos == findInsertPosIndex - 1) {
                insertPos = i + 1;
            }
            if (findInsertPos == findInsertPosIndex) {
                insertPos = i;
                break;
            }
        }
    }
    return insertPos == -1 ? 0 : insertPos;
}

void ModelManagerPrivate::setItemsStateInHomeScreen(int state)
{
    iterateMultiPageModelWithItemCallBack(&launcherModel, [state, this](LauncherItem *item) {
        if (item->type() == LauncherItem::Group &&
                item->state() == LauncherItem::Popup) {
            int groupId = item->id();
            setItemsStateInPopupedGroup(groupId, state);
        } else if (item->type() != LauncherItem::Placeholder) {
            item->setState(state);
        }
    });
}

void ModelManagerPrivate::setItemsStateInTaskbar(int state)
{
    setItemsStateInPopupedGroup(kTaskBarGroupId, state);
}

void ModelManagerPrivate::setItemsStateInPopupedGroup(int groupId, int state)
{
    if (!groupModel.contains(groupId)) {
        qWarning() << "The page model contains the group item, "
                      "but not found in group model. " << groupId;
        return;
    }
    auto *_groupModel = groupModel.value(groupId);
    iterateMultiPageModelWithItemCallBack(_groupModel, [state](LauncherItem *item) {
        if (item->type() != LauncherItem::Placeholder) {
            item->setState(state);
        }
    });
}

void ModelManagerPrivate::addBlankPageToAllModels()
{
    launcherModel.push_back(new PageModel);
    auto it = groupModel.constBegin();
    while (it != groupModel.constEnd()) {
        if (it.key() == kTaskBarGroupId) {
            ++ it;
            continue;
        }
        groupModel[it.key()]->push_back(new PageModel);
        ++ it;
    }
}

void ModelManagerPrivate::deleteBlankPageFromAllModels()
{
    removeEmptyPage(&launcherModel);

    auto it = groupModel.constBegin();
    while (it != groupModel.constEnd()) {
        if (it.key() == kTaskBarGroupId) {
            ++ it;
            continue;
        }
        auto groupModel = it.value();
        removeEmptyPage(groupModel);

        ++ it;
    }
}

std::tuple<QString, QString> ModelManagerPrivate::createNewGroupName(LauncherItem *underItem, LauncherItem *aboveItem)
{
    int theSameCategory = findTheSameNumber(underItem->categories(), aboveItem->categories());
    QString groupName, friendlyName;
    qDebug() << __func__ << underItem->categories() << aboveItem->categories() << theSameCategory;
    if (theSameCategory != -1) {
        groupName = AppCategoryManager::categoryToString(theSameCategory);//dataHandler->categories().value(static_cast<UKUI::BaseItem::Categories>(theSameCategory));
    } else {
        int groupNumber = confMgr.untitledGroupCount() + 1;
        groupName = "Group " + QString::number(groupNumber);
        confMgr.setUntitledGroupCount(groupNumber);
    }
    friendlyName = dataHandler->translatedName(groupName);
    return std::make_tuple(groupName, friendlyName);
}

MultiPageModel *ModelManagerPrivate::createGroup(int groupId,
                                      const QString &groupName,
                                      LauncherItem *underItem)
{
    MultiPageModel *_groupModel = new MultiPageModel;
    PageModel *pageModel = new PageModel;
    LauncherItem *itemToGroup = new LauncherItem(*underItem);
    itemToGroup->setPageIndex(0);
    itemToGroup->setState(globalState);
    if (isAppInTheWhiteList(itemToGroup->desktopFile())) {
        itemToGroup->setUninstallable(false);
    }
    pageModel->push_back(itemToGroup);
    if (underItem->type() != LauncherItem::Group) {
        underItem->setName(groupName);
    }
    groupModel.insert(groupId, _groupModel);
    maxItemCountAllowedInPageOfGroup[groupId] = kMaxItemCountInGroupPage;
    _groupModel->push_back(pageModel);
    _groupModel->push_back(new PageModel);

    underItem->setId(groupId);
    underItem->setDesktopFile("");
    underItem->setType(LauncherItem::Group);
    underItem->setModel(_groupModel);
    return _groupModel;
}

int ModelManagerPrivate::removeAboveItem(int newGroupId, PageModel *newPageModel, LauncherItem *aboveItem)
{
    Q_Q(ModelManager);
    int removeItemGroupId = -1;
    newPageModel->push_back(aboveItem);
    if (launcherModel.at(aboveItem->pageIndex())->contains(aboveItem)) {
        launcherModel.at(aboveItem->pageIndex())->removeOne(aboveItem);
    } else {
        for (int i = 0; i < groupModel.count(); ++i) {
            int groupid = groupModel.keys().at(i);
            if (groupid == newGroupId) {
                continue;
            }
            if (aboveItem->pageIndex() >= groupModel.value(groupid)->count()) {
                continue;
            }
            if (groupModel.value(groupid)->at(aboveItem->pageIndex())->contains(aboveItem)) {
                groupModel.value(groupid)->at(aboveItem->pageIndex())->removeOne(aboveItem);
                removeItemGroupId = groupid;
                q->updateCachedData(groupid);
            }
        }
    }

    newPageModel->last()->setPageIndex(0);
    return removeItemGroupId;
}

void ModelManagerPrivate::updateItemStatusInGroupAfterGroupStateChanged(int groupId, int state)
{
    Q_Q(ModelManager);
    auto _groupModel = groupModel.value(groupId);
    auto *groupItem = item(groupId);
    bool nameVisible = state == LauncherItem::Popup ? true : false;
    int itemState = -1;
    if (state == LauncherItem::Popup) {
        itemState = state;
    } else if (state == LauncherItem::Editable) {
        itemState = LauncherItem::Normal;
    } else if (state == LauncherItem::Normal) {
        q->removePlaceholderItem();
        // 如果分组最小化恢复分组时，分组内只有一个app，则需要删除分组恢复为AppIcon类型
        if (_groupModel->count() == 1 &&
                _groupModel->first()->count() == 1) {
            auto takeGroupModel = groupModel.take(groupId);
            *groupItem = *takeGroupModel->first()->first();
            takeGroupModel->deleteLater();
        }
    } else {
        q->removePlaceholderItem();
    }
    iterateMultiPageModelWithItemCallBack(_groupModel, [itemState, nameVisible](LauncherItem *item) {
        item->setNameVisible(nameVisible);
        if (itemState != -1) {
            item->setState(itemState);
        }
    });
}

void ModelManagerPrivate::iterateMultiPageModelWithItemCallBack(MultiPageModel *model,
                                                                const std::function<void (LauncherItem *)> &callBack)
{
    if (!model) {
        return;
    }
    for (auto &itemsInPage : qAsConst(*model)) {
        for (auto &item : qAsConst(*itemsInPage)) {
            callBack(item);
        }
    }
}

bool ModelManagerPrivate::iterateMultiPageModelWithItemReturnCallBack(MultiPageModel *model,
                                                                      const std::function<bool (LauncherItem *)> &callBack)
{
    if (!model) {
        return false;
    }
    for (auto &itemsInPage : qAsConst(*model)) {
        for (auto &item : qAsConst(*itemsInPage)) {
            if (callBack(item)) {
                return true;
            }
        }
    }
    return false;
}

void ModelManagerPrivate::iterateMultiPageModelWithItemIndexCallBack(MultiPageModel *model,
                                                                     const std::function<void (LauncherItem *, int, int)> &callBack)
{
    if (!model) {
        return;
    }
    for (int pageIndex=0; pageIndex<model->count(); ++pageIndex) {
        for (int itemIndex=0; itemIndex<model->at(pageIndex)->count(); ++itemIndex) {
            callBack(model->at(pageIndex)->at(itemIndex), pageIndex, itemIndex);
        }
    }
}
QPair<int, int> ModelManagerPrivate::itemPosInGroup(const int itemId, const int groupId)
{
    if (!groupModel.contains(groupId))
    {
        qWarning() << __func__ << "groupId not exists !!!";
        return QPair<int, int>(-1, -1);
    }
    int pageCount = groupModel.value(groupId)->count();
    for (int page = 0; page < pageCount; ++page) {
        for (int itemIndex = 0;
             itemIndex < groupModel.value(groupId)->at(page)->count();
             ++itemIndex) {
            if (groupModel.value(groupId)->at(page)->at(itemIndex)->id() == itemId &&
                    groupModel.value(groupId)->at(page)->at(itemIndex)->type() == LauncherItem::AppIcon) {
                return QPair<int, int>(page, itemIndex);
            }
        }
    }
    return QPair<int, int>(-1, -1);
}

void ModelManagerPrivate::takeAppIdFromDesktop(const int itemId, const int pageIndex)
{
    if (pageIndex >= launcherModel.count() || pageIndex < 0) {
        qWarning() << __func__ << "page is valid !!!";
        return;
    }
    auto itemTakeFromDesktop = item(itemId);
    if (itemTakeFromDesktop == nullptr) {
        qWarning() << __func__ << "itemId not find !!!";
        return;
    }
    launcherModel.at(pageIndex)->removeOne(itemTakeFromDesktop);
    if (launcherModel.at(pageIndex)->empty() &&
            itemTakeFromDesktop->state() == LauncherItem::Normal) {
        removeEmptyPage(&launcherModel);
    }
}

void ModelManagerPrivate::takeAppIdFromGroup(const int itemId, const int groupId)
{
    QPair<int, int> itemPos = itemPosInGroup(itemId, groupId);
    int itemPageIndex = itemPos.first;
    int itemIndex = itemPos.second;
    if (itemPageIndex == -1) {
        qWarning() << "itemId or groupId not exists !!!";
        return;
    }
    qDebug() << __func__ << "model itemId=" << groupModel.value(groupId)->at(itemPageIndex)->at(itemIndex)->id();
    auto itemState = groupModel.value(groupId)->at(itemPageIndex)->at(itemIndex)->state();
    groupModel.value(groupId)->at(itemPageIndex)->removeAt(itemIndex);
    if (!needAddIcon) {
        if (groupModel.value(groupId)->at(itemPageIndex)->empty() &&
                itemState == LauncherItem::Normal &&
                groupId != kTaskBarGroupId) {
            removeEmptyPage(groupModel.value(groupId));
        }
        if (getGroupState(groupId) != LauncherItem::Popup) {
            replaceGroupWithAppIcon(groupId);
        }
    } else {
        if (itemState == LauncherItem::Normal) {
            removeEmptyPageFromContainer(groupId);
            if (item(groupId)->containerEditable()) {
                tryAddAddIcon(groupId);
            }
        }
    }
}

//edu-screen
void ModelManagerPrivate::addPageToContainer(const int containerId)
{
    int containerPageCount = groupModel.value(containerId)->count();
    if (containerPageCount <= 0) {
        qWarning() << __func__ << "container is null !!!";
        return;
    }
    if (containerPageCount == 1 ||
            groupModel.value(containerId)->at(containerPageCount-1)->count() > 0) {
        groupModel.value(containerId)->push_back(new PageModel);
    }
}

//edu-screen
void ModelManagerPrivate::removeEmptyPageFromContainer(const int containerId)
{
    auto model = groupModel.value(containerId);
    int maxItemCount = maxItemCountAllowedInPageOfGroup.value(containerId);
    for (int page = 0; page < model->count(); ++page) {
        if (model->count() <= 1) {
            return;
        }
        if (!isContainerPageEmpty(containerId, page)) {
            continue;
        }
        if (page == model->count() - 1) {
            if (model->at(page-1)->count() < maxItemCount ||
                    (model->at(page-1)->count() == maxItemCount &&
                     model->at(page-1)->at(maxItemCount-1)->type() == LauncherItem::AddIcon)) {
                model->removeAt(page);
                --page;
            }
        } else {
            model->removeAt(page);
            updateItemPageIndexAfterDeletePage(model, page);
            --page;
        }
    }
}

LauncherItem* ModelManagerPrivate::taskbarWindowOpenItem(const QString &desktopName)
{
    if (!groupModel.contains(kTaskBarGroupId) ||
            groupModel.value(kTaskBarGroupId)->empty()) {
        qWarning() << "taskbar is empty !!!";
        return nullptr;
    }
    for (const auto taskbarItem : *groupModel.value(0)->at(0)) {
        if (taskbarItem->desktopFile() == desktopName) {
            return taskbarItem;
        }
    }
    return nullptr;
}

//edu-screen
bool ModelManagerPrivate::isContainerPageEmpty(const int containerId, const int pageIndex)
{
    if (!groupModel.contains(containerId) ||
            !pageIndexIsValid(pageIndex, groupModel.value(containerId))) {
        qWarning() << __func__ << "containerId or pageIndex is invalid";
        return false;
    }
    auto pageModel = groupModel.value(containerId)->at(pageIndex);
    if (pageModel->count() == 0 ||
            (pageModel->count() == 1 &&
             pageModel->last()->type() == LauncherItem::AddIcon)) {
        return true;
    }
    return false;
}

void ModelManagerPrivate::updateItemPageIndexAfterDeletePage(MultiPageModel *model,
                                                             const int deletePageIndex)
{
    for (int j = deletePageIndex; j < model->count(); ++j) {
        for (int itemIndex = 0; itemIndex < model->at(j)->count(); ++itemIndex) {
            int groupItemPageIndex = model->at(j)->at(itemIndex)->pageIndex();
            model->at(j)->at(itemIndex)->setPageIndex(groupItemPageIndex - 1);
        }
    }
}
